//////////////////////////////////////////////////////////////////////////
//
// This file is a part of UKROP project.
// Copyright (c) Vladimir Gumenuk
//
// PROJECT:         Xort Custom Debug Engine
// FILE:            DebugProperty.cpp : Implementation of CDebugProperty
// AUTHOR:          Vladimir Gumenuk
// DATE:            5-Jan-2013
// MODIFICATIONS:
//
//////////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "xort.h"
#include "xruntime.h"
#include "DebugProperty.h"
#include "dllmain.h"
#include "DebugStack.h"
#include "StackFrame.h"

// CDebugProperty
CDebugProperty::CDebugProperty()
{
    m_pSymbol     = NULL;
    m_pSelf       = NULL;
    m_pOwner      = NULL;
    m_dwStackBase = 0;
}

CDebugProperty::~CDebugProperty()
{

}

HRESULT CDebugProperty::FinalConstruct()
{
    return S_OK;
}

void CDebugProperty::FinalRelease()
{
}

STDMETHODIMP CDebugProperty::Initialize( IUnknown *pSymbol, DWORD dwStackBase, IUnknown *pSelfPtr, IUnknown *pOwnerPtr, DWORD dwType ) // actually pointer to Symbol is passed
{
    m_pSymbol     = (Symbol*)pSymbol;
    m_pSelf       = (xobject *)pSelfPtr;
    m_pOwner      = (xobject *)pOwnerPtr;
    m_dwStackBase = dwStackBase;
    m_dwType      = dwType;

    if( !m_pSelf && m_pOwner )
    {
        m_pSelf = findSelfPointer( m_pSymbol );
    }

    return S_OK;
}

STDMETHODIMP CDebugProperty::AddProperty( IDebugProperty2 *pProperty ) // actually internal CPropertyInfo passed!
{
    m_listProperties.push_back( pProperty );
    return S_OK;
}

STDMETHODIMP CDebugProperty::GetSymbol( IUnknown **ppOut ) // actually Symbol is returned!
{
    if( !ppOut )
        return E_INVALIDARG;

    if( m_pSymbol ) {
        *ppOut = (IUnknown *)m_pSymbol;
        return S_OK;
    }
    return E_FAIL;
}

STDMETHODIMP CDebugProperty::GetPropertyInfo( DEBUGPROP_INFO_FLAGS dwFields, DWORD dwRadix, DWORD dwTimeout, IDebugReference2 **rgpArgs, DWORD dwArgCount, DEBUG_PROPERTY_INFO *pPropertyInfo )
{
    if( !pPropertyInfo )
        return E_INVALIDARG;

    //ATLTRACE( "CDebugProperty::GetPropertyInfo() -> '%s', fields: 0x%08X\n", m_pSymbol->m_csName.GetBuffer(), dwFields );
    pPropertyInfo->dwAttrib = DBG_ATTRIB_NONE;
    pPropertyInfo->dwFields = DEBUGPROP_INFO_NONE;

    CComPtr<IXProgram> spXProg = _AtlModule.getXProgramNoAddRef();
    SYMBOL_LIST *pListGlobal;
    spXProg->GetGlobalSymbolTable( (IUnknown**)&pListGlobal );
    XRuntime *pRT = NULL;
    spXProg->GetXRuntime( (IUnknown**)&pRT );
    XCELLSARRAY& stack = pRT->get_Stack();

    if( m_dwType == DBG_PROP_STKREG_ROOT )
    {
        pPropertyInfo->dwFields = DEBUGPROP_INFO_PROP; // just return self as root, after that EnumChildren will be called
    }
    else if( m_dwType == DBG_PROP_STKREG )
    {
        if( stack.isValidIndex( m_dwStackBase ) )
        {
            XortElement& val = stack.get( m_dwStackBase );
            FormatRegister( val, (int)m_dwStackBase, dwRadix, pPropertyInfo );
            ATLTRACE( "REG: %S %S\n", pPropertyInfo->bstrName, pPropertyInfo->bstrValue );
        }
    }
    else if( m_dwType == DBG_PROP_LOCALVAR || m_dwType == DBG_PROP_GLOBALSYMBOL || m_dwType == DBG_PROP_SELFPTR )
    {
        switch( m_pSymbol->m_SymKind )
        {
        case sk_var:
        case sk_arg:
            {
                xobject *pXself = NULL;

                pPropertyInfo->bstrName = CComBSTR( m_pSymbol->m_csName ).Detach();
                SymbolRef typeName = FindType( *pListGlobal, m_pSymbol->m_dwType );
                pPropertyInfo->bstrType = CComBSTR( typeName.m_csName ).Detach();
                pPropertyInfo->dwFields = DEBUGPROP_INFO_NAME | DEBUGPROP_INFO_TYPE | DEBUGPROP_INFO_VALUE;

                if( m_pSymbol->m_dwStorage == cSTORAGE_STACK )
                {
                    //ATLTRACE( "varIdx: %d, stkBase: %u\n", m_pSymbol->m_iIndex, m_dwStackBase );
                    DWORD dwIndex = (DWORD)( (int)m_dwStackBase + m_pSymbol->m_iIndex );
                    if( stack.isValidIndex( dwIndex ) )
                    {
                        XortElement& val = stack.get( dwIndex );
                        pXself = ( val.bType == t_p ) ? (xobject *)val._p : NULL;
                        string csVal = xFormatElementValue( val, dwRadix );
                        pPropertyInfo->bstrValue = CComBSTR( csVal.GetBuffer() ).Detach();
                    } else {
                        pPropertyInfo->bstrValue = CComBSTR( L"invalid" ).Detach();
                    }
                }
                else if( m_pSymbol->m_dwStorage == cSTORAGE_DATA )
                {
                    XortElement& val = pRT->get_DataCell( (dword)m_pSymbol->m_iIndex );
                    pXself = ( val.bType == t_p ) ? (xobject *)val._p : NULL;
                    string csVal = xFormatElementValue( val, dwRadix );
                    pPropertyInfo->bstrValue = CComBSTR( csVal.GetBuffer() ).Detach();
                }
                else if( m_pSymbol->m_dwStorage == cSTORAGE_MEMBER && m_pOwner )
                {
                    XortElement val;
                    if( m_pOwner->Get( m_pSymbol->m_iIndex, val ) == XORT_OK )
                    {
                        pXself = ( val.bType == t_p ) ? (xobject *)val._p : NULL;
                        string csVal = xFormatElementValue( val, dwRadix );
                        pPropertyInfo->bstrValue = CComBSTR( csVal.GetBuffer() ).Detach();
                    }
                } else {
                    ATLASSERT( FALSE );
                }

                if( typeName.m_SymKind == sk_obj && ( pXself || m_pSelf ) )
                {
                    pPropertyInfo->dwFields |= DEBUGPROP_INFO_VALUE_AUTOEXPAND | DEBUGPROP_INFO_ATTRIB;
                    pPropertyInfo->dwAttrib = DBG_ATTRIB_OBJ_IS_EXPANDABLE;
                }
                
                break;
            }

        case sk_obj: // we have object 'self' and now we are inside some member method...
            {
                if( m_pSelf )
                {
                    pPropertyInfo->bstrName = CComBSTR( L"self" ).Detach();
                    pPropertyInfo->bstrType = CComBSTR( m_pSymbol->m_csName ).Detach();

                    XortElement val; val.bType = t_p; val._p = m_pSelf;
                    string csVal = xFormatElementValue( val, dwRadix );
                    val.bType = 0; val._p = NULL; // otherwise it will be released in destructor..

                    pPropertyInfo->bstrValue = CComBSTR( csVal.GetBuffer() ).Detach();

                    pPropertyInfo->dwFields = DEBUGPROP_INFO_NAME | DEBUGPROP_INFO_TYPE | DEBUGPROP_INFO_VALUE | DEBUGPROP_INFO_VALUE_AUTOEXPAND | DEBUGPROP_INFO_ATTRIB;
                    pPropertyInfo->dwAttrib = DBG_ATTRIB_OBJ_IS_EXPANDABLE;
                }
                break;
            }

        case sk_proc:
            {
                pPropertyInfo->bstrName = CComBSTR( m_pSymbol->m_csName ).Detach();
                SymbolRef typeName = FindType( *pListGlobal, m_pSymbol->m_dwType );
                pPropertyInfo->bstrType = CComBSTR( typeName.m_csName ).Detach();
                pPropertyInfo->dwFields = DEBUGPROP_INFO_NAME | DEBUGPROP_INFO_TYPE;
                break;
            }

        default:
            //ATLASSERT( FALSE ); // Later I can support typenames and all other stuff, but I don't think they are needed.
            break;
        }
    }


    CComQIPtr<IDebugProperty2> spProp( this );
    pPropertyInfo->pProperty = spProp.Detach();

    return S_OK;
}

void CDebugProperty::FormatRegister( const XortElement& xCell, int iIndex, UINT nRadix, DEBUG_PROPERTY_INFO *pProp )
{
    char szBuf[MAX_PATH];

    nRadix = 16; //nRadix == 0 ? 10 : nRadix;

    sprintf( szBuf, "REG%d:", iIndex );
    pProp->bstrName = CComBSTR( szBuf ).Detach();
    
    if( xCell.bAccess == sys_Frame )
    {
        /*if( nRadix == 16 )
            sprintf( szBuf, "0x%02X-0x%02X", xCell.bType, xCell.bAccess );
        else
            sprintf( szBuf, "%u-%u", xCell.bType, xCell.bAccess );
        
        //pProp->bstrType = CComBSTR( szBuf ).Detach();*/
        pProp->bstrFullName = CComBSTR( L"SystemReg0" ).Detach();
        pProp->bstrType = CComBSTR( L"CPU" ).Detach();

        sprintf( szBuf, "0x%08X", xCell._asp );
        pProp->bstrValue = CComBSTR( szBuf ).Detach();

    } else {

        sprintf( szBuf, "UserReg%d:", iIndex );
        pProp->bstrFullName = CComBSTR( szBuf ).Detach();

        char szType[32];
        char szVal[64];

        switch( xCell.bType )
        {
        case t_v:  strcpy( szType, "t_v" );  sprintf( szBuf, "%s", "ERR_INVALID_TYPE" ); break;
        case t_s1: strcpy( szType, "t_s1" ); sprintf( szBuf, "0x%s", _itoa( xCell._s1, szVal, nRadix ) ); break;
        case t_u1: strcpy( szType, "t_u1" ); sprintf( szBuf, "0x%s", _itoa( xCell._u1, szVal, nRadix ) ); break;
        case t_s2: strcpy( szType, "t_s2" ); sprintf( szBuf, "0x%s", _itoa( xCell._s2, szVal, nRadix ) ); break;
        case t_u2: strcpy( szType, "t_u2" ); sprintf( szBuf, "0x%s", _itoa( xCell._u2, szVal, nRadix ) ); break;
        case t_s4: strcpy( szType, "t_s4" ); sprintf( szBuf, "0x%s", _itoa( xCell._s4, szVal, nRadix ) ); break;
        case t_u4: strcpy( szType, "t_u4" ); sprintf( szBuf, "0x%s", _itoa( xCell._u4, szVal, nRadix ) ); break;
        case t_s8: strcpy( szType, "t_s8" ); sprintf( szBuf, "0x%s", _i64toa_s( xCell._s8, szVal, 64, nRadix ) ); break;
        case t_u8: strcpy( szType, "t_u8" ); sprintf( szBuf, "0x%s", _i64toa_s( xCell._u8, szVal, 64, nRadix ) ); break;
        case t_r4: strcpy( szType, "t_r4" ); sprintf( szBuf, "%s", _gcvt( xCell._r4, 6,  szVal ) ); break;
        case t_r8: strcpy( szType, "t_r8" ); sprintf( szBuf, "%s", _gcvt( xCell._r8, 12,  szVal ) ); break;
        case t_p:  strcpy( szType, "t_p" );  sprintf( szBuf, "0x%s", _itoa( xCell._u4, szVal, nRadix ) ); break;
        default:   strcpy( szType, "<?>" );  sprintf( szBuf, "%s", "ERR_UNKNOWN_TYPE" ); break;
        }

        //pProp->bstrType  = CComBSTR( szType ).Detach();
        pProp->bstrType = CComBSTR( L"CPU" ).Detach();
        pProp->bstrValue = CComBSTR( szBuf ).Detach();
    }
    
    //pProp->bstrName = CComBSTR( L"EAX" ).Detach();
    //pProp->bstrValue = CComBSTR( L"0xAABBCCDD" ).Detach();
    pProp->dwFields = DEBUGPROP_INFO_FULLNAME | DEBUGPROP_INFO_NAME | DEBUGPROP_INFO_VALUE | DEBUGPROP_INFO_TYPE; // | DEBUGPROP_INFO_ATTRIB;
    //pProp->dwAttrib = DBG_ATTRIB_STORAGE_REGISTER;
}

// E_SETVALUE_VALUE_CANNOT_BE_SET The string could not be converted into a property value, or the property value could not be set.
// E_SETVALUE_VALUE_IS_READONLY The property is read-only.
STDMETHODIMP CDebugProperty::SetValueAsString( LPCOLESTR pszValue,DWORD dwRadix, DWORD dwTimeout )
{
    return E_NOTIMPL;
}

// E_SETVALUEASREFERENCE_NOTSUPPORTED  Setting the value from a reference is not supported.
// E_SETVALUE_VALUE_CANNOT_BE_SET  The value cannot be set, as this property refers to a method.
// E_SETVALUE_VALUE_IS_READONLY  The value is read-only and cannot be set.
STDMETHODIMP CDebugProperty::SetValueAsReference( IDebugReference2 **rgpArgs, DWORD dwArgCount, IDebugReference2 *pValue, DWORD dwTimeout )
{
    return E_NOTIMPL;
}

STDMETHODIMP CDebugProperty::EnumChildren( DEBUGPROP_INFO_FLAGS dwFields, DWORD dwRadix, REFGUID guidFilter, 
    DBG_ATTRIB_FLAGS dwAttribFilter, LPCOLESTR pszNameFilter, DWORD dwTimeout, IEnumDebugPropertyInfo2 **ppEnum )
{
    HRESULT hr = E_FAIL;
    
    CComPtr<IXEnumPropertyInfo> spXEnum;
    if( FAILED( hr = CEnumDebugPropertyInfo::Create( &spXEnum ) ) )
        return hr;

    // IDE always passed GUID_NULL... :(
    //if( guidFilter == guidFilterRegisters )
    CComPtr<IXProgram> spXProg = _AtlModule.getXProgramNoAddRef();
    SYMBOL_LIST *pListGlobal;
    spXProg->GetGlobalSymbolTable( (IUnknown**)&pListGlobal );
    SymbolRef typeName = FindType( *pListGlobal, m_pSymbol->m_dwType );

    if( typeName.m_SymKind == sk_obj )
    {
        if( !m_pSelf )
            m_pSelf = findSelfPointer( m_pSymbol );

        if( m_pSelf && m_listProperties.size() == 0 ) // build member variables
        {
            SYMBOL_LIST::iterator theIter = typeName.m_listMembers.begin();
            Symbol *pSym = theIter.item();

            while( pSym )
            {
                if( pSym->m_SymKind == sk_var ) // take only variables
                {
                    CComPtr<IXProperty> spXPoperty;
                    if( SUCCEEDED( CDebugProperty::Create( &spXPoperty ) ) )
                    {
                        spXPoperty->Initialize( (IUnknown*)pSym, m_dwStackBase, NULL, (IUnknown*)m_pSelf, DBG_PROP_LOCALVAR );

                        CComQIPtr<IDebugProperty2> spProp( spXPoperty );
                        m_listProperties.push_front( spProp ); // member goes in reverse order after compiler... put them back to normal
                    }
                }

                theIter++;
                pSym = theIter.item();
            }
        }

        if( m_listProperties.size() > 0 )
        {
            PROPERTIES_LIST::iterator theIter = m_listProperties.begin();
            IDebugProperty2* pProp = theIter.item();

            while( pProp )
            {
                CPropertyInfo *pDbgProp = new CPropertyInfo();
                pDbgProp->AddRef();

                if( SUCCEEDED( hr = pProp->GetPropertyInfo( dwFields, dwRadix, dwTimeout, NULL, 0, pDbgProp ) ) )
                {
                    spXEnum->AddProperty( pDbgProp );
                }

                pDbgProp->Release();

                theIter++;
                pProp = theIter.item();
            }
        }
    }

    if( SUCCEEDED( hr ) )
        hr = spXEnum->QueryInterface( IID_IEnumDebugPropertyInfo2, (void**)ppEnum );

    return hr;
}

STDMETHODIMP CDebugProperty::GetParent( IDebugProperty2 **ppParent )
{
    if( !m_spParent )
        return S_GETPARENT_NO_PARENT;
    return m_spParent->QueryInterface( IID_IDebugProperty2, (void**)ppParent );
}

// For example, if this property describes an object that implements ClassRoot but which 
// is actually an instantiation of ClassDerived that is derived from ClassRoot, 
// then this method returns an IDebugProperty2 object describing the ClassDerived object.
STDMETHODIMP CDebugProperty::GetDerivedMostProperty( IDebugProperty2 **ppDerivedMost )
{
    return S_GETDERIVEDMOST_NO_DERIVED_MOST;
}

STDMETHODIMP CDebugProperty::GetMemoryBytes( IDebugMemoryBytes2 **ppMemoryBytes )
{
    return S_GETMEMORYBYTES_NO_MEMORY_BYTES;
}

STDMETHODIMP CDebugProperty::GetMemoryContext( IDebugMemoryContext2 **ppMemory )
{
    return S_GETMEMORYCONTEXT_NO_MEMORY_CONTEXT;
}

// Returns the size, in bytes, of the property value.
STDMETHODIMP CDebugProperty::GetSize( DWORD *pdwSize )
{
    return S_GETSIZE_NO_SIZE;
}

// The reference to the object/value/variable that persists during all program lifetime
// Do not return the reference to the local variable
STDMETHODIMP CDebugProperty::GetReference( IDebugReference2 **ppReference )
{
    return E_GETREFERENCE_NO_REFERENCE;
}

STDMETHODIMP CDebugProperty::GetExtendedInfo( REFGUID guidExtendedInfo, VARIANT *pExtendedInfo )
{
    return S_GETEXTENDEDINFO_NO_EXTENDEDINFO;
}

xobject * CDebugProperty::findSelfPointer( Symbol *pSymbol )
{
    xobject *pRetObj = NULL;

    XRuntime *pRT = NULL;
    _AtlModule.getXProgramNoAddRef()->GetXRuntime( (IUnknown**)&pRT );

    if( pSymbol->m_dwStorage == cSTORAGE_STACK )
    {
        XCELLSARRAY& stack = pRT->get_Stack();

        int iIdx = (int)m_dwStackBase + pSymbol->m_iIndex;
        if( stack.isValidIndex( (dword)iIdx ) )
        {
            XortElement& xSelf = stack.get( (dword)iIdx );
            if( xSelf.bType == t_p )
            {
                pRetObj = (xobject*)xSelf._p;
            } else {
                ATLASSERT( FALSE );
            }
        }
    }
    else if( pSymbol->m_dwStorage == cSTORAGE_DATA )
    {
        XCELLSARRAY& data  = pRT->get_Data();
        if( data.isValidIndex( (dword)pSymbol->m_iIndex ) )
        {
            XortElement& xSelf = data.get( (dword)pSymbol->m_iIndex );
            if( xSelf.bType == t_p )
            {
                pRetObj = (xobject*)xSelf._p;
            } else {
                ATLASSERT( FALSE );
            }
        }
    }
    else if( m_pSymbol->m_dwStorage == cSTORAGE_MEMBER ) // object is a child inside some other object
    {
        if( pSymbol->m_pOwner && m_pOwner )
        {
            XortElement val;
            if( m_pOwner->Get( pSymbol->m_iIndex, val ) == XORT_OK )
            {
                pRetObj = ( val.bType == t_p ) ? (xobject *)val._p : NULL;
            } // don't worry about -ref after XortElement destructor - we do not collect additional object refs outside XVM...
        } else {
            ATLASSERT( FALSE );
        }
    }

    return pRetObj;
}